﻿using System;
using System.ServiceProcess;
using System.Text;
using Microsoft.Win32;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Net.Security;
using System.Net.Sockets;
using System.Globalization;
using System.Threading;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Management;
using System.Security;
using System.Reflection;
using WinSCP;

namespace MyUPSMonitor
{
    public partial class MyUPSMonitor : ServiceBase
    {
        private RegistryKey myAPCAgentReg;
        private System.Collections.Generic.Dictionary<string,Thread> myQueryCycles=new Dictionary<string,Thread>();
        private System.Collections.Generic.Dictionary<string, Thread> myActionThreads = new Dictionary<string, Thread>();
        static Mutex mxActionThreads = new Mutex(false);
        private string decryptPassword(byte[] encoded)
        {
            if (encoded == null)
            {
                return "";
            }
            string salt = "";
            string rst = "";
            foreach (byte b in encoded)
            {
                foreach (char c in Encoding.ASCII.GetChars(new byte[] { (byte)(255 - b) }))
                {
                    rst += c;
                }
            }
            foreach (char c in Encoding.ASCII.GetChars(Convert.FromBase64String(rst)))
            {
                salt += c.ToString();
            }
            rst = "";
            for (int ii = 0; ii < salt.Length; ii += 5)
            {
                rst += (char)Math.Sqrt(uint.Parse(salt.Substring(ii, 5)));
            }
            salt = "";
            foreach (char c in Encoding.Unicode.GetChars(Convert.FromBase64String(rst)))
            {
                salt += c.ToString();
            }
            return salt;
        }

        public MyUPSMonitor()
        {
            InitializeComponent();
            myAPCAgentReg = Registry.LocalMachine.CreateSubKey("SOFTWARE\\MyUPSMonitor");
            myQueryCycles = new System.Collections.Generic.Dictionary<string, Thread>();
            EventLog.Source = "MyUPSMonitor";
            this.CanPauseAndContinue = true;
        }

        private void ShutdownWindows(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisHostName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisHostName"];
            string thisUID = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisUID"];
            string thisPWD = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisPWD"];
            string thisTimeOut = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"];
            string newState = "";
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            System.Net.IPHostEntry thisHost = System.Net.Dns.GetHostEntry(thisHostName);
            ConnectionOptions wmiOptions = new ConnectionOptions
            {
                Username = thisUID,
                Password = thisPWD,
                Impersonation = ImpersonationLevel.Impersonate,
                Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                EnablePrivileges = true
            };
            ManagementScope wmiHost = new ManagementScope(@"\\" + thisHost.HostName + @"\root\cimv2", wmiOptions);
            SelectQuery wmiQuery = new SelectQuery("select * from win32_networkadapterconfiguration");
            string thisMACAddress = "";
            string thisSubNet = "";
            try
            {
                ManagementObjectSearcher wmiSearcher = new ManagementObjectSearcher(wmiHost, wmiQuery);
                foreach (ManagementObject wmiIPConfig in wmiSearcher.Get())
                {
                    bool thisNIC = false;
                    string[] thisIPs = (string[])wmiIPConfig.GetPropertyValue("IPAddress");
                    string[] thisSubNets = (string[])wmiIPConfig.GetPropertyValue("IPSubnet");
                    if (!(thisIPs == null))
                    {
                        for (int i = 0; i < thisIPs.Length; i++)
                        {
                            if (thisIPs[i] == thisHost.AddressList[0].ToString())
                            {
                                thisNIC = true;
                                thisSubNet = thisSubNets[i];
                            }
                        }
                    }
                    if (thisNIC)
                    {
                        thisMACAddress = (string)wmiIPConfig.GetPropertyValue("MACAddress");
                    }
                }
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(thisActionName + ": Can not communicate with the target.\r\n" + e.Message, EventLogEntryType.Error, 91);
                thisMACAddress = "Error";
            }
            if (string.IsNullOrEmpty(thisMACAddress))
            {
                EventLog.WriteEntry(thisActionName + ": Can not find the MACAddress.", EventLogEntryType.Error, 90);
                mxActionThreads.WaitOne();
                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
                mxActionThreads.ReleaseMutex();
            }
            else
            {
                if (thisMACAddress != "Error")
                {
                    newState= thisHost.AddressList[0].ToString() + "/" + thisSubNet + "/" + thisMACAddress;
                    try
                    {
                        ManagementClass wmiOSClass = new ManagementClass(wmiHost, new ManagementPath("Win32_OperatingSystem"), null);
                        wmiOSClass.Get();
                        wmiOSClass.Scope.Options.EnablePrivileges = true;
                        ManagementBaseObject wmiShutdownParams = wmiOSClass.GetMethodParameters("Win32Shutdown");
                        wmiShutdownParams["Flags"] = 12;
                        wmiShutdownParams["Reserved"] = 0;
                        EventLog.WriteEntry(thisActionName + ": Shutting down target.\r\n" , EventLogEntryType.Information, 2);
                        foreach (ManagementObject eachOS in wmiOSClass.GetInstances())
                        {
                            eachOS.InvokeMethod("Win32Shutdown",wmiShutdownParams,null);
                        }
                    }
                    catch (Exception e)
                    {
                        EventLog.WriteEntry(thisActionName + ": Can not shutdown target.\r\n" + e.Message, EventLogEntryType.Error, 92);
                        newState = "";
                    }
                }
                System.Collections.Generic.Dictionary<string, string> thisParamDict = new Dictionary<string, string>();
                thisParamDict.Add("thisAgent", thisAgent);
                thisParamDict.Add("thisActionName", thisActionName);
                thisParamDict.Add("thisDirection", "Down");
                thisParamDict.Add("newState", newState);
                thisParamDict.Add("thisTimeOut", thisTimeOut);
                mxActionThreads.WaitOne();
                if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Monitor"))
                {
                    if ((myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                    {
                        myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Monitor");
                        myActionThreads.Add(thisAgent + ":" + thisActionName + "_Monitor", new Thread(new ParameterizedThreadStart(CheckOSUpDown)));
                        myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].Start(thisParamDict);
                    }
                }
                else
                {
                    myActionThreads.Add(thisAgent + ":" + thisActionName + "_Monitor", new Thread(new ParameterizedThreadStart(CheckOSUpDown)));
                    myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].Start(thisParamDict);
                }
                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
                mxActionThreads.ReleaseMutex();
            }
        }

        private void StartByWOL(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisTimeOut = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"];
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            string thisState = "";
            try { thisState = (string)thisActionKey.GetValue("State"); }
            catch { };
            if (!string.IsNullOrEmpty(thisState))
            {
                string thisIP = thisState.Split("/".ToCharArray())[0];
                string thisSN = thisState.Split("/".ToCharArray())[1];
                string thisMAC = thisState.Split("/".ToCharArray())[2];
                thisMAC = thisMAC.Replace(":", "").Replace("-", "").Replace(" ", "").Replace("/", "");
                byte[] ipBytes = new byte[4];
                byte[] snBytes = new byte[4];
                byte[] targetBytes = new byte[4];
                for (int i = 0; i < 4; i++)
                {
                    ipBytes[i] = byte.Parse(thisIP.Split(".".ToCharArray())[i]);
                    snBytes[i] = byte.Parse(thisSN.Split(".".ToCharArray())[i]);
                    targetBytes[i] = (byte)(ipBytes[i] | (~snBytes[i]));
                }

                UdpClient myUDPClient = new UdpClient();
                myUDPClient.Connect(new IPEndPoint(new IPAddress(targetBytes), 9));
                myUDPClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                int counter = 0;
                byte[] bytes = new byte[1024];
                for (int y = 0; y < 6; y++)
                    bytes[counter++] = 0xFF;
                for (int y = 0; y < 16; y++)
                {
                    int i = 0;
                    for (int z = 0; z < 6; z++)
                    {
                        bytes[counter++] =
                            byte.Parse(thisMAC.Substring(i, 2),
                            NumberStyles.HexNumber);
                        i += 2;
                    }
                }
                EventLog.WriteEntry(thisActionName + ": Starting target.\r\n", EventLogEntryType.Information, 3);
                myUDPClient.Send(bytes, 1024);
                System.Collections.Generic.Dictionary<string, string> thisParamDict = new Dictionary<string, string>();
                thisParamDict.Add("thisAgent", thisAgent);
                thisParamDict.Add("thisActionName", thisActionName);
                thisParamDict.Add("thisDirection", "Up");
                thisParamDict.Add("newState", "");
                thisParamDict.Add("thisTimeOut", thisTimeOut);
                mxActionThreads.WaitOne();
                if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Monitor"))
                {
                    if ((myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                    {
                        myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Monitor");
                        myActionThreads.Add(thisAgent + ":" + thisActionName + "_Monitor", new Thread(new ParameterizedThreadStart(CheckOSUpDown)));
                        myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].Start(thisParamDict);
                    }
                }
                else
                {
                    myActionThreads.Add(thisAgent + ":" + thisActionName + "_Monitor", new Thread(new ParameterizedThreadStart(CheckOSUpDown)));
                    myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].Start(thisParamDict);
                }
                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Up");
                mxActionThreads.ReleaseMutex();
            }
            else
            {
                EventLog.WriteEntry(thisActionName + ": Target already started.\r\n", EventLogEntryType.Warning, 53);
                mxActionThreads.WaitOne();
                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Up");
                mxActionThreads.ReleaseMutex();
            }
        }

        private void CheckOSUpDown(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisDirection = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisDirection"];
            string newState = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["newState"];
            Int16 thisTimeOut = Int16.Parse(((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"]);
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            string thisHostName="";
            string thisUID="";
            string thisPWD="";
            string thisState = "";
            string thisType = "";
            try { thisState = (string)thisActionKey.GetValue("State"); }
            catch { };
            DateTime startTameStamp = DateTime.UtcNow;
            while (thisState != newState)
            {
                try { thisHostName = (string)thisActionKey.GetValue("HostName"); }
                catch { };
                try { thisType = (string)thisActionKey.GetValue("Type"); }
                catch { };
                try { thisUID = (string)thisActionKey.GetValue("UserID"); }
                catch { };
                try { thisState = (string)thisActionKey.GetValue("State"); }
                catch { };
                try { thisPWD = decryptPassword((byte[])thisActionKey.GetValue("Password")); }
                catch { };
                try { thisState = (string)thisActionKey.GetValue("State"); }
                catch { };
                System.Net.IPHostEntry thisHost = System.Net.Dns.GetHostEntry(thisHostName);
                if (thisType.ToLower() == "windows")
                {
                    ConnectionOptions wmiOptions = new ConnectionOptions
                    {
                        Username = thisUID,
                        Password = thisPWD,
                        Impersonation = ImpersonationLevel.Impersonate,
                        Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                        EnablePrivileges = true
                    };
                    ManagementScope wmiHost = new ManagementScope(@"\\" + thisHost.HostName + @"\root\cimv2", wmiOptions);
                    try
                    {
                        ManagementClass wmiOSClass = new ManagementClass(wmiHost, new ManagementPath("Win32_OperatingSystem"), null);
                        ManagementObjectCollection wmiOSes = wmiOSClass.GetInstances();
                        if (thisDirection == "Up")
                        {
                            EventLog.WriteEntry(thisActionName + ": Communication established with target.\r\n", EventLogEntryType.Information, 4);
                            thisActionKey.SetValue("State", newState);
                            thisState = newState;
                        }
                        else
                        {
                            if (startTameStamp.AddSeconds(thisTimeOut) < DateTime.UtcNow)
                            {
                                EventLog.WriteEntry(thisActionName + ": Target timed out stopping.\r\n", EventLogEntryType.Information, 5);
                                thisActionKey.SetValue("State", newState);
                                thisState = newState;
                            }
                            else
                            {
                                EventLog.WriteEntry(thisActionName + ": Target is still stopping.\r\n", EventLogEntryType.Information, 5);
                                Thread.Sleep(2048);
                            }
                        }
                    }
                    catch
                    {
                        if (thisDirection == "Up")
                        {
                            if (startTameStamp.AddSeconds(thisTimeOut) < DateTime.UtcNow)
                            {
                                EventLog.WriteEntry(thisActionName + ": Target timed out starting.\r\n", EventLogEntryType.Information, 5);
                                thisActionKey.SetValue("State", newState);
                                thisState = newState;
                            }
                            else
                            {
                                EventLog.WriteEntry(thisActionName + ": Target is still starting.\r\n", EventLogEntryType.Information, 5);
                                Thread.Sleep(2048);
                            }
                        }
                        else
                        {
                            EventLog.WriteEntry(thisActionName + ": Target stopped.\r\n", EventLogEntryType.Information, 4);
                            thisActionKey.SetValue("State", newState);
                            thisState = newState;
                        }
                    }
                }
                else
                {
                    string sshHostName = executeSSH(thisHost.HostName, thisUID, thisPWD, "uname");
                    if ((sshHostName.ToLower() + "      ").Substring(0, 6) != "error:")
                    {
                        if (thisDirection == "Up")
                        {
                            EventLog.WriteEntry(thisActionName + ": Communication established with target.\r\n", EventLogEntryType.Information, 4);
                            thisActionKey.SetValue("State", newState);
                            thisState = newState;
                        }
                        else
                        {
                            EventLog.WriteEntry(thisActionName + ": Target is still stopping.\r\n", EventLogEntryType.Information, 5);
                            Thread.Sleep(2048);
                        }
                    }
                    else
                    {
                        if (thisDirection == "Up")
                        {
                            EventLog.WriteEntry(thisActionName + ": Target is still starting.\r\n", EventLogEntryType.Information, 5);
                            Thread.Sleep(2048);
                        }
                        else
                        {
                            EventLog.WriteEntry(thisActionName + ": Target stopped.\r\n", EventLogEntryType.Information, 4);
                            thisActionKey.SetValue("State", newState);
                            thisState = newState;
                        }
                    }
                }
            }
            mxActionThreads.WaitOne();
            myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Monitor");
            mxActionThreads.ReleaseMutex();
        }

        private void MonitorWindowsService(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisHostName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisHostName"];
            string thisServiceName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisServiceName"];
            string thisUID = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisUID"];
            string thisPWD = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisPWD"];
            string thisState = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["WaitFor"].ToLower();
            Int16 thisTimeOut = Int16.Parse(((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"]);
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            if (string.IsNullOrEmpty(thisHostName)) thisHostName = "localhost";
            System.Net.IPHostEntry thisHost = System.Net.Dns.GetHostEntry(thisHostName);
            ConnectionOptions wmiOptions;
            if (thisHost.HostName == System.Net.Dns.GetHostEntry("localhost").HostName)
            {
                wmiOptions = new ConnectionOptions
                {
                    Impersonation = ImpersonationLevel.Impersonate,
                    Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                    EnablePrivileges = true
                };
            }
            else
            {
                wmiOptions = new ConnectionOptions
                {
                    Username = thisUID,
                    Password = thisPWD,
                    Impersonation = ImpersonationLevel.Impersonate,
                    Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                    EnablePrivileges = true
                };
            }
            string thisStatus = "x";
            ManagementScope wmiHost = new ManagementScope(@"\\" + thisHost.HostName + @"\root\cimv2", wmiOptions);
            DateTime startTimeStamp = DateTime.UtcNow;
            while (!(thisState == thisStatus.ToLower()))
            {
                try
                {
                    WqlObjectQuery serviceSearch = new WqlObjectQuery("SELECT * FROM Win32_Service  WHERE Name=\"" + thisServiceName + "\"");
                    ManagementObjectSearcher serviceFind = new ManagementObjectSearcher(wmiHost, serviceSearch);
                    foreach (ManagementObject eachService in serviceFind.Get())
                    {
                        thisStatus = eachService.GetPropertyValue("State").ToString();
                        if (thisStatus.ToLower() == thisState)
                        {
                            EventLog.WriteEntry(thisActionName + ": Service " + thisServiceName + " on host " + thisHostName + " is " + thisStatus + ".", EventLogEntryType.Information, 11);
                            if (thisState == "running")
                            {
                                thisActionKey.SetValue("State", "");
                            }
                            else thisActionKey.SetValue("State", "Stopped");
                        }
                        else
                        {
                            if (startTimeStamp.AddSeconds(thisTimeOut) < DateTime.UtcNow)
                            {
                                EventLog.WriteEntry(thisActionName + ": Service " + thisServiceName + " on host " + thisHostName + " timed out " + thisStatus + ".", EventLogEntryType.Warning, 14);
                                thisState = thisStatus.ToLower();
                            }
                            else Thread.Sleep(1117);
                        }
                    }

                }
                catch (Exception e)
                {
                    EventLog.WriteEntry(thisActionName + ": Can not query service " + thisServiceName + " on host " + thisHostName + ".\r\n" + e.Message, EventLogEntryType.Error, 89);
                    thisActionKey.SetValue("State", "");
                    thisStatus = thisState;
                }
            }
            mxActionThreads.WaitOne();
            if (thisState.ToLower() == "running")
            {
                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_UpMonitor");
            }
            else
            {
                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_DownMonitor");
            }
            mxActionThreads.ReleaseMutex();
        }

        private void StopWindowsService(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisHostName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisHostName"];
            string thisServiceName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisServiceName"];
            string thisUID = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisUID"];
            string thisPWD = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisPWD"];
            string thisTimeOut = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"];
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            if (string.IsNullOrEmpty(thisHostName)) thisHostName = "localhost";
            System.Net.IPHostEntry thisHost = System.Net.Dns.GetHostEntry(thisHostName);
            ConnectionOptions wmiOptions;
            if (thisHost.HostName==System.Net.Dns.GetHostEntry("localhost").HostName)
            {
                wmiOptions = new ConnectionOptions
                {
                    Impersonation = ImpersonationLevel.Impersonate,
                    Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                    EnablePrivileges = true
                };
            }
            else
            {
                wmiOptions = new ConnectionOptions
                {
                    Username = thisUID,
                    Password = thisPWD,
                    Impersonation = ImpersonationLevel.Impersonate,
                    Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                    EnablePrivileges = true
                };
            }
            ManagementScope wmiHost = new ManagementScope(@"\\" + thisHost.HostName + @"\root\cimv2", wmiOptions);
            thisActionKey.SetValue("State", "Stopping service");
            try
            {
                WqlObjectQuery serviceSearch = new WqlObjectQuery("SELECT * FROM Win32_Service  WHERE Name=\"" + thisServiceName + "\"");
                ManagementObjectSearcher serviceFind = new ManagementObjectSearcher(wmiHost, serviceSearch);
                EventLog.WriteEntry(thisActionName + ": Stopping service " + thisServiceName + " on host " + thisHostName + ".", EventLogEntryType.Information, 11);
                foreach (ManagementObject eachService in serviceFind.Get())
                {
                    eachService.InvokeMethod("StopService", new object[] { });
                }
                System.Collections.Generic.Dictionary<string, string> thisParamDict = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict);
                thisParamDict.Add("WaitFor", "Stopped");
                mxActionThreads.WaitOne();
                if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_DownMonitor"))
                {
                    if ((myActionThreads[thisAgent + ":" + thisActionName + "_DownMonitor"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                    {
                        myActionThreads.Remove(thisAgent + ":" + thisActionName + "_DownMonitor");
                        myActionThreads.Add(thisAgent + ":" + thisActionName + "_DownMonitor", new Thread(new ParameterizedThreadStart(MonitorWindowsService)));
                        myActionThreads[thisAgent + ":" + thisActionName + "_DownMonitor"].Start(thisParamDict);
                    }
                }
                else
                {
                    myActionThreads.Add(thisAgent + ":" + thisActionName + "_DownMonitor", new Thread(new ParameterizedThreadStart(MonitorWindowsService)));
                    myActionThreads[thisAgent + ":" + thisActionName + "_DownMonitor"].Start(thisParamDict);
                }
                mxActionThreads.ReleaseMutex();
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(thisActionName + ": Can not stop service " + thisServiceName + " on host " + thisHostName + ".\r\n" + e.Message, EventLogEntryType.Error, 82);
                thisActionKey.SetValue("State", "");
            }
            mxActionThreads.WaitOne();
            myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
            mxActionThreads.ReleaseMutex();
        }

        private void StartWindowsService(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisHostName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisHostName"];
            string thisServiceName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisServiceName"];
            string thisUID = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisUID"];
            string thisPWD = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisPWD"];
            string thisTimeOut = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"];
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            if (string.IsNullOrEmpty(thisHostName)) thisHostName = "localhost";
            System.Net.IPHostEntry thisHost = System.Net.Dns.GetHostEntry(thisHostName);
            ConnectionOptions wmiOptions;
            if (thisHost.HostName == System.Net.Dns.GetHostEntry("localhost").HostName)
            {
                wmiOptions = new ConnectionOptions
                {
                    Impersonation = ImpersonationLevel.Impersonate,
                    Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                    EnablePrivileges = true
                };
            }
            else
            {
                wmiOptions = new ConnectionOptions
                {
                    Username = thisUID,
                    Password = thisPWD,
                    Impersonation = ImpersonationLevel.Impersonate,
                    Authentication = System.Management.AuthenticationLevel.PacketPrivacy,
                    EnablePrivileges = true
                };
            }
            ManagementScope wmiHost = new ManagementScope(@"\\" + thisHost.HostName + @"\root\cimv2", wmiOptions);
            thisActionKey.SetValue("State", "Starting service");
            try
            {
                WqlObjectQuery serviceSearch = new WqlObjectQuery("SELECT * FROM Win32_Service  WHERE Name=\"" + thisServiceName + "\"");
                ManagementObjectSearcher serviceFind = new ManagementObjectSearcher(wmiHost, serviceSearch);
                EventLog.WriteEntry(thisActionName + ": Starting service " + thisServiceName + " on host " + thisHostName + ".", EventLogEntryType.Information, 12);
                foreach (ManagementObject eachService in serviceFind.Get())
                {
                    eachService.InvokeMethod("StartService", new object[] { });
                }
                System.Collections.Generic.Dictionary<string, string> thisParamDict = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict);
                thisParamDict.Add("WaitFor", "Running");
                mxActionThreads.WaitOne();
                if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_UpMonitor"))
                {
                    if ((myActionThreads[thisAgent + ":" + thisActionName + "_UpMonitor"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                    {
                        myActionThreads.Remove(thisAgent + ":" + thisActionName + "_UpMonitor");
                        myActionThreads.Add(thisAgent + ":" + thisActionName + "_UpMonitor", new Thread(new ParameterizedThreadStart(MonitorWindowsService)));
                        myActionThreads[thisAgent + ":" + thisActionName + "_UpMonitor"].Start(thisParamDict);
                    }
                }
                else
                {
                    myActionThreads.Add(thisAgent + ":" + thisActionName + "_UpMonitor", new Thread(new ParameterizedThreadStart(MonitorWindowsService)));
                    myActionThreads[thisAgent + ":" + thisActionName + "_UpMonitor"].Start(thisParamDict);
                }
                mxActionThreads.ReleaseMutex();
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(thisActionName + ": Can not start service " + thisServiceName + " on host " + thisHostName + ".\r\n" + e.Message, EventLogEntryType.Error, 83);
                thisActionKey.SetValue("State", "");
            }
            mxActionThreads.WaitOne();
            myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Up");
            mxActionThreads.ReleaseMutex();
        }

        private string executeSSH(string thisHostName, string thisUID, string thisPWD, string thisCommand)
        {
            string result = "";
            try
            {
                WinSCP.SessionOptions wscpSessionOption = new SessionOptions
                {
                    Protocol = Protocol.Scp,
                    HostName = thisHostName,
                    UserName = thisUID,
                    Password = thisPWD,
                    GiveUpSecurityAndAcceptAnySshHostKey = true,
                };
                WinSCP.Session wscpSession = new Session();
                wscpSession.Open(wscpSessionOption);
                WinSCP.CommandExecutionResult wscpResult = wscpSession.ExecuteCommand(thisCommand);
                if (wscpResult.IsSuccess)
                {
                    result = wscpResult.Output;
                }
                else
                {
                    result = "Error: " + wscpResult.ErrorOutput;
                }
            }
            catch (Exception e)
            {
                result = "Error: " + e.Message;
            }
            return result;
        }

        private void StopLinuxWithSSH(object myParamsDict)
        {
            string thisAgent = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisAgent"];
            string thisActionName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisActionName"];
            string thisHostName = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisHostName"];
            string thisUID = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisUID"];
            string thisPWD = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisPWD"];
            string thisTimeOut = ((System.Collections.Generic.Dictionary<string, string>)myParamsDict)["thisTimeOut"];
            string newState = "";
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions", true);
            RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName, true);
            System.Net.IPHostEntry thisHost = System.Net.Dns.GetHostEntry(thisHostName);
            string getMAC = "ifconfig | grep " + thisHost.AddressList[0].ToString() + " -B 1|grep -Eo '[0-9a-fA-F]{1,2}[:-][0-9a-fA-F]{1,2}[:-][0-9a-fA-F]{1,2}[:-][0-9a-fA-F]{1,2}[:-][0-9a-fA-F]{1,2}[:-][0-9a-fA-F]{1,2}'";
            string getSubnet = "ifconfig | grep " + thisHost.AddressList[0].ToString() + " | grep -oE 'Mask:[0-9.]+'|grep -oE '[0-9.]+'";
            string mACAddress = executeSSH(thisHost.HostName, thisUID, thisPWD, getMAC);
            if ((mACAddress.ToLower() + "      ").Substring(0, 6) != "error:")
            {
                string subnet = executeSSH(thisHost.HostName, thisUID, thisPWD, getSubnet);
                newState = thisHost.AddressList[0].ToString() + "/" + subnet + "/" + mACAddress;
                string shutdownresult = executeSSH(thisHost.HostName, thisUID, thisPWD, "shutdown -P now");
            }
            else
            {
                EventLog.WriteEntry("Error executing SSH command on " + thisHostName + ".\r\n" + mACAddress.Substring(6), EventLogEntryType.Error, 53);
            }
            System.Collections.Generic.Dictionary<string, string> thisParamDict = new Dictionary<string, string>();
            thisParamDict.Add("thisAgent", thisAgent);
            thisParamDict.Add("thisActionName", thisActionName);
            thisParamDict.Add("thisDirection", "Down");
            thisParamDict.Add("newState", newState);
            thisParamDict.Add("thisTimeOut", thisTimeOut);
            mxActionThreads.WaitOne();
            if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Monitor"))
            {
                if ((myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                {
                    myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Monitor");
                    myActionThreads.Add(thisAgent + ":" + thisActionName + "_Monitor", new Thread(new ParameterizedThreadStart(CheckOSUpDown)));
                    myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].Start(thisParamDict);
                }
            }
            else
            {
                myActionThreads.Add(thisAgent + ":" + thisActionName + "_Monitor", new Thread(new ParameterizedThreadStart(CheckOSUpDown)));
                myActionThreads[thisAgent + ":" + thisActionName + "_Monitor"].Start(thisParamDict);
            }
            myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
            mxActionThreads.ReleaseMutex();
        }

        private void checkActions(string thisAgent, string myStatus, int minutesRemaining, bool onBattery)
        {
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + thisAgent + "\\Actions",true);
            foreach (string thisActionName in myRegKey.GetSubKeyNames())
            {
                RegistryKey thisActionKey = myRegKey.OpenSubKey(thisActionName,true);
                int thisDown = 0;
                int thisUp = 0;
                int thisTimeOut = 0;
                string thisType = "";
                string thisHostName = "";
                string thisService = "";
                string thisUID = "";
                string thisState = "";
                string thisPWD = "";
                try { thisDown = (int)thisActionKey.GetValue("Down"); }
                catch { };
                try { thisUp = (int)thisActionKey.GetValue("Up"); }
                catch { };
                try { thisTimeOut = (int)thisActionKey.GetValue("TimeOut"); }
                catch { };
                try { thisType = (string)thisActionKey.GetValue("Type"); }
                catch { };
                try { thisHostName = (string)thisActionKey.GetValue("HostName"); }
                catch { };
                try { thisService = (string)thisActionKey.GetValue("Service"); }
                catch { };
                try { thisUID = (string)thisActionKey.GetValue("UserID"); }
                catch { };
                try { thisState = (string)thisActionKey.GetValue("State"); }
                catch { };
                try { thisPWD = decryptPassword((byte[])thisActionKey.GetValue("Password")); }
                catch { };
                System.Collections.Generic.Dictionary<string, string> thisParamDict = new Dictionary<string, string>();
                thisParamDict.Add("thisAgent", thisAgent);
                thisParamDict.Add("thisActionName", thisActionName);
                thisParamDict.Add("thisHostName", thisHostName);
                thisParamDict.Add("thisUID", thisUID);
                thisParamDict.Add("thisPWD", thisPWD);
                thisParamDict.Add("thisTimeOut", thisTimeOut.ToString());
                if ((minutesRemaining <= thisDown) & onBattery)
                {
                    EventLog.WriteEntry(thisActionName + ": Shutdown conditions met. ", EventLogEntryType.Warning, 1);
                    if (thisType == "Windows" & !string.IsNullOrEmpty(thisHostName))
                    {
                        mxActionThreads.WaitOne();
                        if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Down"))
                        {
                            if ((myActionThreads[thisAgent + ":" + thisActionName + "_Down"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                            {
                                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
                                myActionThreads.Add(thisAgent + ":" + thisActionName + "_Down", new Thread(new ParameterizedThreadStart(ShutdownWindows)));
                                myActionThreads[thisAgent + ":" + thisActionName + "_Down"].Start(thisParamDict);
                            }
                        }
                        else
                        {
                            myActionThreads.Add(thisAgent + ":" + thisActionName + "_Down", new Thread(new ParameterizedThreadStart(ShutdownWindows)));
                            myActionThreads[thisAgent + ":" + thisActionName + "_Down"].Start(thisParamDict);
                        }
                        mxActionThreads.ReleaseMutex();
                    }
                    if (thisType == "Unix-Linux" & !string.IsNullOrEmpty(thisHostName))
                    {
                        mxActionThreads.WaitOne();
                        if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Down"))
                        {
                            if ((myActionThreads[thisAgent + ":" + thisActionName + "_Down"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                            {
                                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
                                myActionThreads.Add(thisAgent + ":" + thisActionName + "_Down", new Thread(new ParameterizedThreadStart(StopLinuxWithSSH)));
                                myActionThreads[thisAgent + ":" + thisActionName + "_Down"].Start(thisParamDict);
                            }
                        }
                        else
                        {
                            myActionThreads.Add(thisAgent + ":" + thisActionName + "_Down", new Thread(new ParameterizedThreadStart(StopLinuxWithSSH)));
                            myActionThreads[thisAgent + ":" + thisActionName + "_Down"].Start(thisParamDict);
                        }
                        mxActionThreads.ReleaseMutex();
                    }
                    if (thisType == "Service")
                    {
                        thisParamDict.Add("thisServiceName", thisService);
                        mxActionThreads.WaitOne();
                        if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Down"))
                        {
                            if ((myActionThreads[thisAgent + ":" + thisActionName + "_Down"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                            {
                                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Down");
                                myActionThreads.Add(thisAgent + ":" + thisActionName + "_Down", new Thread(new ParameterizedThreadStart(StopWindowsService)));
                                myActionThreads[thisAgent + ":" + thisActionName + "_Down"].Start(thisParamDict);
                            }
                        }
                        else
                        {
                            myActionThreads.Add(thisAgent + ":" + thisActionName + "_Down", new Thread(new ParameterizedThreadStart(StopWindowsService)));
                            myActionThreads[thisAgent + ":" + thisActionName + "_Down"].Start(thisParamDict);
                        }
                        mxActionThreads.ReleaseMutex();
                    }
                }
                if ((minutesRemaining >= thisUp) & (!onBattery) & (!string.IsNullOrEmpty(thisState)) )
                {
                    EventLog.WriteEntry(thisActionName + ": Startup conditions met. ", EventLogEntryType.Warning, 11);
                    if ((thisType == "Windows") || (thisType == "Unix-Linux"))
                    {
                        mxActionThreads.WaitOne();
                        if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Up"))
                        {
                            if ((myActionThreads[thisAgent + ":" + thisActionName + "_Up"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                            {
                                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Up");
                                myActionThreads.Add(thisAgent + ":" + thisActionName + "_Up", new Thread(new ParameterizedThreadStart(StartByWOL)));
                                myActionThreads[thisAgent + ":" + thisActionName + "_Up"].Start(thisParamDict);
                            }
                        }
                        else
                        {
                            myActionThreads.Add(thisAgent + ":" + thisActionName + "_Up", new Thread(new ParameterizedThreadStart(StartByWOL)));
                            myActionThreads[thisAgent + ":" + thisActionName + "_Up"].Start(thisParamDict);
                        }
                        mxActionThreads.ReleaseMutex();
                    }
                    if ((thisType == "Service") & (thisState.ToLower()=="stopped"))
                    {
                        thisParamDict.Add("thisServiceName", thisService);
                        mxActionThreads.WaitOne();
                        if (myActionThreads.ContainsKey(thisAgent + ":" + thisActionName + "_Up"))
                        {
                            if ((myActionThreads[thisAgent + ":" + thisActionName + "_Up"].ThreadState & System.Threading.ThreadState.Stopped) == System.Threading.ThreadState.Stopped)
                            {
                                myActionThreads.Remove(thisAgent + ":" + thisActionName + "_Up");
                                myActionThreads.Add(thisAgent + ":" + thisActionName + "_Up", new Thread(new ParameterizedThreadStart(StartWindowsService)));
                                myActionThreads[thisAgent + ":" + thisActionName + "_Up"].Start(thisParamDict);
                            }
                        }
                        else
                        {
                            myActionThreads.Add(thisAgent + ":" + thisActionName + "_Up", new Thread(new ParameterizedThreadStart(StartWindowsService)));
                            myActionThreads[thisAgent + ":" + thisActionName + "_Up"].Start(thisParamDict);
                        }
                        mxActionThreads.ReleaseMutex();
                    }
                }
            }
        }

        private void runchecks(object thisAgentName)
        {
            bool endCycle = false;
            string myAgentName = (string)thisAgentName;
            string myStatus;
            bool onBattery;
            int upsRemaining;
            int rfon;
            int rfoff;
            UInt32 upsFactor;
            string agentDLL;
            string agentClass;
            Assembly agentAssambly;
            Type thisAgent;
            string functionGetMinutesRemaining;
            object[] paramsForGetMinutesRemaining;
            string functionIsOnBattery;
            object[] paramsForIsOnBattery;
            string functionGetStatus;
            object[] paramsForGetStatus;
            ParameterInfo[] myParams;
            RegistryKey myRegKey = myAPCAgentReg.OpenSubKey("Agents\\" + myAgentName);
            while (!endCycle)
            {
                agentDLL = string.Empty;
                upsFactor = 1000;
                rfoff = 0;
                rfon = 0;
                upsRemaining = 0;
                myStatus = "Internal Comm. Error";
                onBattery = true;
                try
                {
                    agentDLL = (string)myRegKey.GetValue("DLLPath");
                    upsFactor = (UInt32)myRegKey.GetValue("TimeFactor");
                    rfon = (int)myRegKey.GetValue("RefreshOnLine");
                    rfoff = (int)myRegKey.GetValue("RefreshOffLine");
                }
                catch { }
                if (string.IsNullOrEmpty(agentDLL))
                {
                    endCycle = true;
                    EventLog.WriteEntry(myAgentName + " No Agent DLL is defined, Exiting.", EventLogEntryType.Error, 999);
                }
                else
                {
                    try
                    {
                        agentClass = (string)myRegKey.GetValue("AgentClass");
                        functionGetMinutesRemaining = (string)myRegKey.GetValue("GetRemainingFunction");
                        functionIsOnBattery = (string)myRegKey.GetValue("IsOnBatteryFunction");
                        functionGetStatus = (string)myRegKey.GetValue("GetStatusFunction");
                        agentAssambly = Assembly.LoadFile(agentDLL);
                        thisAgent = agentAssambly.GetType(agentClass, true, true);
                        myParams = ((MethodInfo)thisAgent.GetMember(functionGetMinutesRemaining)[0]).GetParameters();
                        paramsForGetMinutesRemaining = new object[myParams.Length];
                        for (int i = 0; i < myParams.Length; i++)
                        {
                            if (myParams[i].Name == "Password")
                            {
                                paramsForGetMinutesRemaining[i] = decryptPassword((byte[])myRegKey.GetValue("Password"));
                            }
                            else
                            {
                                paramsForGetMinutesRemaining[i] = myRegKey.GetValue(myParams[i].Name);
                            }
                        }
                        myParams = ((MethodInfo)thisAgent.GetMember(functionGetStatus)[0]).GetParameters();
                        paramsForGetStatus = new object[myParams.Length];
                        for (int i = 0; i < myParams.Length; i++)
                        {
                            if (myParams[i].Name == "Password")
                            {
                                paramsForGetStatus[i] = decryptPassword((byte[])myRegKey.GetValue("Password"));
                            }
                            else
                            {
                                paramsForGetStatus[i] = myRegKey.GetValue(myParams[i].Name);
                            }
                        }
                        myParams = ((MethodInfo)thisAgent.GetMember(functionIsOnBattery)[0]).GetParameters();
                        paramsForIsOnBattery = new object[myParams.Length];
                        for (int i = 0; i < myParams.Length; i++)
                        {
                            if (myParams[i].Name == "Password")
                            {
                                paramsForIsOnBattery[i] = decryptPassword((byte[])myRegKey.GetValue("Password"));
                            }
                            else
                            {
                                paramsForIsOnBattery[i] = myRegKey.GetValue(myParams[i].Name);
                            }
                        }
                        upsRemaining = (int)thisAgent.InvokeMember(functionGetMinutesRemaining, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, paramsForGetMinutesRemaining);
                        myStatus = (string)thisAgent.InvokeMember(functionGetStatus, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, paramsForGetStatus);
                        onBattery = (bool)thisAgent.InvokeMember(functionIsOnBattery, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, paramsForIsOnBattery);
                    }
                    catch (Exception e)
                    {
                        EventLog.WriteEntry(myAgentName + " Exception in agent query or initialization:\r\n" + e.Message, EventLogEntryType.Error, 998);
                        myStatus = "Internal Comm. Error";
                    }
                    if (!endCycle)
                    {
                        if (myStatus.Trim().Length == 0)
                        {
                            EventLog.WriteEntry(myAgentName + " UPS checked, No communication.", EventLogEntryType.Warning, 99);
                            Thread.Sleep(1000 * 23);
                        }
                        else
                            if (myStatus == "Internal Comm. Error")
                            {
                                Thread.Sleep(1000 * 23);
                            }
                            else
                            {
                                if (onBattery)
                                {
                                    EventLog.WriteEntry(myAgentName + " UPS checked, running on Battery runtime remainig: " + upsRemaining.ToString(), EventLogEntryType.Warning, 2);
                                }
                                else
                                {
                                    EventLog.WriteEntry(myAgentName + " UPS checked, State is " + myStatus + ", runtime remainig: " + upsRemaining.ToString(), EventLogEntryType.Information, 0);
                                }
                                checkActions(myAgentName, myStatus, upsRemaining, onBattery);
                                if (!onBattery)
                                {
                                    if (rfon == 0)
                                    {
                                        Thread.Sleep((int)(upsRemaining * upsFactor * 12 ) + 5000);
                                    }
                                    else
                                    {
                                        Thread.Sleep(rfon * 60 * 1000);
                                    }
                                }
                                else
                                {
                                    if (rfoff == 0)
                                    {
                                        Thread.Sleep((int)(upsRemaining * upsFactor * 4) + 2000);
                                    }
                                    else
                                    {
                                        Thread.Sleep(rfoff * 60 * 1000);
                                    }
                                }
                            }
                    }
                }
            }
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                myQueryCycles.Clear();
                foreach (string agent in myAPCAgentReg.OpenSubKey("Agents").GetSubKeyNames())
                {
                    myQueryCycles.Add(agent, new Thread(new ParameterizedThreadStart(runchecks)));
                    myQueryCycles[agent].Start(agent);
                }
            }
            catch (Exception e)
            {
                EventLog.WriteEntry("MyUPSMonitor Exception while starting:\r\n" + e.Message, EventLogEntryType.Error, 29);
            }
        }
        protected override void OnStop()
        {
            foreach (Thread myThread in myQueryCycles.Values)
            {
                myThread.Abort();
            }
        }
        protected override void OnPause()
        {
            foreach (Thread myThread in myQueryCycles.Values)
            {
                myThread.Abort();
            }
            base.OnPause();
        }
        protected override void OnContinue()
        {
            myQueryCycles.Clear();
            foreach (string agent in myAPCAgentReg.OpenSubKey("Agents").GetSubKeyNames())
            {
                myQueryCycles.Add(agent, new Thread(new ParameterizedThreadStart(runchecks)));
                myQueryCycles[agent].Start(agent);
            }
            base.OnContinue();
        }

        private bool actionStopService(string myAgentName, string hostName, string serviceName, string userName, string password, int timeOutSec)
        {
            List<string> strLocals = new List<string>();
            strLocals.Add("");
            strLocals.Add("localhost");
            strLocals.Add("127.0.0.1");
            strLocals.Add(System.Environment.MachineName.ToLower());
            if (strLocals.Contains(hostName.ToLower()))
            {
                return actionStopService(myAgentName, serviceName, timeOutSec);
            }
            if (userName.Trim().Length * password.Trim().Length == 0)
            {
                return actionStopService(myAgentName, hostName, serviceName, timeOutSec);
            }
            string myuid = userName;
            MyNetworkConnection myNetCon;
            bool myNetIsConnected = false;
            try
            {
                myNetCon = new MyNetworkConnection(hostName, myuid, password);
                myNetIsConnected = true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error connecting to remote system:" + hostName + "\r\n\r\nException:\r\n" + e.Message, EventLogEntryType.Error, 10);
                return false;
            }
            if (!myNetIsConnected)
            {
                return false;
            }
            try
            {
                ServiceController targetService = new System.ServiceProcess.ServiceController(serviceName, hostName);
                targetService.Stop();
                targetService.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, timeOutSec, 0));
                myNetCon.Dispose();
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error stopping remote service: \"" + serviceName + "\" on " + hostName + "\r\n\r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 12);
                return false;
            }

        }
        private bool actionStopService(string myAgentName, string hostName, string serviceName, int timeOutSec)
        {
            List<string> strLocals = new List<string>();
            strLocals.Add("");
            strLocals.Add("localhost");
            strLocals.Add("127.0.0.1");
            strLocals.Add(System.Environment.MachineName.ToLower());
            if (strLocals.Contains(hostName.ToLower()))
            {
                return actionStopService(myAgentName, serviceName, timeOutSec);
            }
            else
            {
                try
                {
                    ServiceController targetService = new System.ServiceProcess.ServiceController(serviceName, hostName);
                    targetService.Stop();
                    targetService.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, timeOutSec, 0));
                    return true;
                }
                catch (Exception e)
                {
                    EventLog.WriteEntry(myAgentName + " Error stopping remote service: \"" + serviceName + "\" on " + hostName + "\r\n\r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 12);
                    return false;
                }
            }
        }
        private bool actionStopService(string myAgentName, string serviceName, int timeOutSec)
        {
            try
            {
                ServiceController targetService = new System.ServiceProcess.ServiceController(serviceName);
                targetService.Stop();
                targetService.WaitForStatus(ServiceControllerStatus.Stopped, new TimeSpan(0, 0, 0, timeOutSec, 0));
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error stopping local service: " + serviceName + "\r\n\r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 11);
                return false;
            }
        }

        private bool actionStartService(string myAgentName, string hostName, string serviceName, string userName, string password, int timeOutSec)
        {
            List<string> strLocals = new List<string>();
            strLocals.Add("");
            strLocals.Add("localhost");
            strLocals.Add("127.0.0.1");
            strLocals.Add(System.Environment.MachineName.ToLower());
            if (strLocals.Contains(hostName.ToLower()))
            {
                return actionStartService(myAgentName, serviceName, timeOutSec);
            }
            if (userName.Trim().Length * password.Trim().Length == 0)
            {
                return actionStartService(myAgentName, hostName, serviceName, timeOutSec);
            }
            string myuid = userName;
            MyNetworkConnection myNetCon;
            bool myNetIsConnected = false;
            try
            {
                myNetCon = new MyNetworkConnection(hostName, myuid, password);
                myNetIsConnected = true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error connecting to remote system:" + hostName + "\r\n\r\nException:\r\n" + e.Message, EventLogEntryType.Error, 10);
                return false;
            }
            if (!myNetIsConnected)
            {
                return false;
            }
            try
            {
                ServiceController targetService = new System.ServiceProcess.ServiceController(serviceName, hostName);
                targetService.Start();
                targetService.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeOutSec, 0));
                myNetCon.Dispose();
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error starting remote service: \"" + serviceName + "\" on " + hostName + "\r\n\r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 13);
                return false;
            }

        }
        private bool actionStartService(string myAgentName, string hostName, string serviceName, int timeOutSec)
        {
            List<string> strLocals = new List<string>();
            strLocals.Add("");
            strLocals.Add("localhost");
            strLocals.Add("127.0.0.1");
            strLocals.Add(System.Environment.MachineName.ToLower());
            if (strLocals.Contains(hostName.ToLower()))
            {
                return actionStartService(myAgentName, serviceName, timeOutSec);
            }
            else
            {
                try
                {
                    ServiceController targetService = new System.ServiceProcess.ServiceController(serviceName, hostName);
                    targetService.Start();
                    targetService.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeOutSec, 0));
                    return true;
                }
                catch (Exception e)
                {
                    EventLog.WriteEntry(myAgentName + " Error starting remote service: \"" + serviceName + "\" on " + hostName + "\r\n\r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 13);
                    return false;
                }
            }
        }
        private bool actionStartService(string myAgentName, string serviceName, int timeOutSec)
        {
            try
            {
                ServiceController targetService = new System.ServiceProcess.ServiceController(serviceName);
                targetService.Start();
                targetService.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 0, timeOutSec, 0));
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error starting local service: " + serviceName + "\r\n\r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 14);
                return false;
            }
        }

        private bool actionShutdown(string myAgentName)
        {
            try
            {
                System.Diagnostics.Process.Start("shutdown", "/s /t 0");
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error shutting down local system! \r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 9);
                return false;
            }
        }
        private bool actionShutdown(string myAgentName,string hostName)
        {
            List<string> strLocals = new List<string>();
            strLocals.Add("");
            strLocals.Add("localhost");
            strLocals.Add("127.0.0.1");
            strLocals.Add(System.Environment.MachineName.ToLower());
            if (strLocals.Contains(hostName.ToLower()))
            {
                return actionShutdown(myAgentName);
            }
            try
            {
                System.Diagnostics.Process.Start("shutdown", "/s /t 0 /m \\\\"+hostName);
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error shutting down remote system " + hostName +"! \r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 9);
                return false;
            }
        }
        private bool actionShutdown(string myAgentName,string hostName, string userName, string password)
        {
            List<string> strLocals = new List<string>();
            strLocals.Add("");
            strLocals.Add("localhost");
            strLocals.Add("127.0.0.1");
            strLocals.Add(System.Environment.MachineName.ToLower());
            if (strLocals.Contains(hostName.ToLower()))
            {
                return actionShutdown(myAgentName);
            }
            if (userName.Trim().Length * password.Trim().Length == 0)
            {
                return actionShutdown(myAgentName, hostName);
            }
            string myuid = userName;
            MyNetworkConnection myNetCon;
            bool myNetIsConnected = false;
            try
            {
                myNetCon = new MyNetworkConnection(hostName, myuid, password);
                myNetIsConnected = true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error connecting to remote system:" + hostName + "\r\n\r\nException:\r\n" + e.Message, EventLogEntryType.Error, 10);
                return false;
            }
            if (!myNetIsConnected)
            {
                return false;
            }
            try
            {
                System.Diagnostics.Process.Start("shutdown", "/s /t 0 /m \\\\"+hostName);
                return true;
            }
            catch (Exception e)
            {
                EventLog.WriteEntry(myAgentName + " Error shutting down remote system " + hostName +"! \r\n Exception:\r\n" + e.Message, EventLogEntryType.Error, 9);
                return false;
            }
        }

        private bool actionStartSystem(string myAgentName, string mACAddress)
        {
            string myMAC = (new Regex("[^0-9a-f]")).Replace(mACAddress.ToLower(), "");
            UdpClient client = new UdpClient();
            client.Connect(new IPAddress(0xffffffff), 65534);
            client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 0);
            int counter = 0;
            byte[] packet = new byte[1024];
            for (int y = 0; y < 6; y++)
                packet[counter++] = 0xFF;
            for (int y = 0; y < 16; y++)
            {
                int i = 0;
                for (int z = 0; z < 6; z++)
                {
                    packet[counter++] = byte.Parse(myMAC.Substring(i, 2), NumberStyles.HexNumber);
                    i += 2;
                }
            }
            EventLog.WriteEntry(myAgentName + " Starting remote system with MAC: " + mACAddress + " (" + myMAC + ").", EventLogEntryType.Information, 3);
            int reterned_value = client.Send(packet, 1024);
            return true;
        }
    }

    public class MyNetworkConnection : IDisposable
    {
        public MyNetworkConnection(string networkName, string userName, string password)
        {
            _netName = networkName;
            NETRESOURCE netResource = new NETRESOURCE();
            netResource.dwScope = ResourceScope.RESOURCE_GLOBALNET;
            netResource.dwType = ResourceType.RESOURCETYPE_DISK;
            netResource.dwDisplayType = ResourceDisplayType.RESOURCEDISPLAYTYPE_SHARE;
            netResource.lpLocalName = networkName;
            int result = WNetAddConnection2(netResource, password, userName, 0);
            if (result != 0) { throw new IOException("Connection Error!", result); }
        }

        private string _netName;
        private bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    //Dispose managed
                }
                WNetCancelConnection2(_netName, 0, true);
                disposed = true;
            }
        }
        ~MyNetworkConnection()
        {
            Dispose(false);
        }

        [StructLayout(LayoutKind.Sequential)]
        private class NETRESOURCE
        {
            public ResourceScope dwScope = 0;
            public ResourceType dwType = 0;
            public ResourceDisplayType dwDisplayType = 0;
            public ResourceUsage dwUsage = 0;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpLocalName = null;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpRemoteName = null;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpComment = null;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpProvider;
        };
        public enum ResourceScope
        {
            RESOURCE_CONNECTED = 1,
            RESOURCE_GLOBALNET,
            RESOURCE_REMEMBERED,
            RESOURCE_RECENT,
            RESOURCE_CONTEXT
        };
        public enum ResourceType
        {
            RESOURCETYPE_ANY,
            RESOURCETYPE_DISK,
            RESOURCETYPE_PRINT,
            RESOURCETYPE_RESERVED
        };
        public enum ResourceUsage
        {
            RESOURCEUSAGE_CONNECTABLE = 0x00000001,
            RESOURCEUSAGE_CONTAINER = 0x00000002,
            RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
            RESOURCEUSAGE_SIBLING = 0x00000008,
            RESOURCEUSAGE_ATTACHED = 0x00000010,
            RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
        };
        public enum ResourceDisplayType
        {
            RESOURCEDISPLAYTYPE_GENERIC,
            RESOURCEDISPLAYTYPE_DOMAIN,
            RESOURCEDISPLAYTYPE_SERVER,
            RESOURCEDISPLAYTYPE_SHARE,
            RESOURCEDISPLAYTYPE_FILE,
            RESOURCEDISPLAYTYPE_GROUP,
            RESOURCEDISPLAYTYPE_NETWORK,
            RESOURCEDISPLAYTYPE_ROOT,
            RESOURCEDISPLAYTYPE_SHAREADMIN,
            RESOURCEDISPLAYTYPE_DIRECTORY,
            RESOURCEDISPLAYTYPE_TREE,
            RESOURCEDISPLAYTYPE_NDSCONTAINER
        };
        public enum ResourceConnection
        {
            CONNECT_UPDATE_PROFILE = 1,
            CONNECT_UPDATE_RECENT = 2,
            CONNECT_TEMPORARY = 4,
            CONNECT_INTERACTIVE = 8,
            CONNECT_PROMPT = 0X10,
            CONNECT_REDIRECT = 0X80,
            CONNECT_CURRENT_MEDIA = 0X200,
            CONNECT_COMMAND_LINE = 0X800,
            CONNECT_CMD_SAVECRED = 0X1000,
            CONNECT_CRED_RESET = 0X2000

        };
        [DllImport("mpr.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
        private static extern int WNetAddConnection2(NETRESOURCE lpNetResource,
            [MarshalAs(UnmanagedType.LPStr)]  string lpPassword,
            [MarshalAs(UnmanagedType.LPStr)]  string lpUserName, int dwFlags);
        [DllImport("mpr.dll")]
        static extern int WNetCancelConnection2(string lpName, Int32 dwFlags, bool bForce);
    }

}
